package org.example.collection;

import org.junit.Test;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

/**
 * CopyOnWriteArrayList 是 ArrayList 的一个线程安全的变体，
 * 其中所有可变操作（add、set 等等）都是通过对底层数组进行一次新的复制来实现的。
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/8/6 8:23
 */
@SuppressWarnings("all")
public class CopyOnWriteArrayListTest {

    /**
     * 操作 ArrayList 一样
     */
    @Test
    public void testCopyOnWriteArrayList1() {
        CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
        for (int i = 1; i <= 10; i++) {
            copyOnWriteArrayList.add(i);
        }
        System.out.println(copyOnWriteArrayList);//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        for (int i = 0; i < copyOnWriteArrayList.size(); ) {
            Integer remove = copyOnWriteArrayList.remove(i);
            //1	2	3	4	5	6	7	8	9	10
            System.out.print(remove + "\t");
        }
    }

    /**
     * 验证 CopyOnWriteArrayList 的并发线程安全
     * 1、假设有 1000 个号(total=1000)，由50个窗口(windows=50)同时卖，每个窗口卖规定卖20个号.
     * 2、最后得结果如果是 CopyOnWriteArrayList 为空，且各个线程没有卖重复的号，则说明并发正常.
     * 3、如果换成 ArrayList ，则立马出现错误数据.
     */
    @Test
    public void testCopyOnWriteArrayList2() {
        CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<Integer>();
        int total = 1000;
        int windows = 50;
        int count = 20;

        for (int i = 1; i <= total; i++) {
            copyOnWriteArrayList.add(i);
        }

        for (int i = 1; i <= windows; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        int i = 0;
                        while (i++ < count) {
                            Object remove = copyOnWriteArrayList.remove(0);
                            System.out.println(Thread.currentThread().getName() + ":" + remove);
                            Thread.sleep(200);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(copyOnWriteArrayList);//[]
    }


}
